/*=========================================================================

  Program:   Visualization Toolkit
  Module:    DRR

=========================================================================*/
//
// This simple example shows how to do basic rendering and pipeline
// creation using C++.
//

#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkProperty.h"
#include "vtkCamera.h"
//For DRR generation
#include "vtkProp3D.h"
#include "vtkVolumeTextureMapper3D.h"
#include "vtkStructuredPointsReader.h"
#include "vtkMetaImageReader.h"
#include "vtkVolumeProperty.h"
#include "vtkPiecewiseFunction.h"
#include "vtkColorTransferFunction.h"
#include "vtkVolume.h"

//For custom interaction style
#include "vtkCommand.h"
#include "vtkInteractorStyleTrackballCamera.h"

//For capturing a projection of the volume as a 2D image
#include "vtkWindowToImageFilter.h"
#include "vtkPNGWriter.h"
#include "vtkJPEGWriter.h"


void usage () {
  std::cerr << "\n";
  std::cerr << "Usage: DRR <options> inputVolume3D outputImage2D\n";
  std::cerr << "       Projects a 3D volume (mha format) onto a 2D image (png format). \n\n";
  std::cerr << "       where <options> is one or more of the following:\n\n";
  std::cerr << "\n       NOT IMPLEMENTED YET\n\n\n";
  // std::cerr << "       <-h>                     Display (this) usage information\n";
  // std::cerr << "       <-v>                     Verbose output [default: no]\n";
  // std::cerr << "       <-dbg>                   Debugging output [default: no]\n";
  // std::cerr << "       <-n int>                 The number of scales to apply [default: 2]\n";
  // std::cerr << "       <-maxScale int>          The scale factor corresponding to max resolution [default: 1]\n";
  // std::cerr << "       <-step float float>      Maximum and minimum step sizes [default: 4 and 0.01]\n";
  // std::cerr << "       <-fl float>              Focal length or source to image distance [default: 400mm]\n";
  // std::cerr << "       <-t float float float>   Translation parameter of the camera \n";
  // std::cerr << "       <-rx float>              Rotation around x,y,z axis in degrees \n";
  // std::cerr << "       <-ry float>\n";
  // std::cerr << "       <-rz float>\n";
  // std::cerr << "       <-normal float float>    The 2D projection normal position [default: 0x0mm]\n";
  // std::cerr << "       <-cor float float float> The centre of rotation relative to centre of volume\n";
  // std::cerr << "       <-threshold float>       Threshold [default: 0]\n";
	std::cerr << "       EXAMPLE: DRR.exe vert1.vtk projection.png \n\n";
	std::cerr << "       When the volume is rendered, pres 'u' to create a projection from the desired pose. \n";
	std::cerr << "       Press 'q' to quit. \n\n";
  std::cerr << "                                by  bryan conrad\n";
  std::cerr << "                                conrabp@ortho.ufl.edu\n";
  std::cerr << "                                (University of Florida)\n\n";
  exit(1);
}

// Callback for the interaction
class vtkMyCallback : public vtkCommand
{
public:
  static vtkMyCallback *New() 
    { 
			
			return new vtkMyCallback; };

	vtkMyCallback() {
    this->Interactor = 0; 
		this->RenWin = 0;};

	void SetFilename(char *projectionFileName) {
		this->ProjectionFileName = projectionFileName; };

	void SetInteractor(vtkRenderWindowInteractor *interactor) {
		this->Interactor = interactor; };
	
	vtkRenderWindowInteractor *GetInteractor() {
    return this->Interactor; };
  
	void SetRenderWindow(vtkRenderWindow *renWin) {
		this->RenWin = renWin; };
	
	vtkRenderWindow *GetRenWin() {
    return this->RenWin; };


	virtual void Execute(vtkObject *caller, unsigned long, void*)
    {
			vtkRenderWindowInteractor *rwi = this->GetInteractor();			
			switch (rwi->GetKeyCode()) 
			{
			case 'u' :      
			case 'U' :
				{
					if (this->ProjectionFileName)
						cout << "Saving Projection..." << this->ProjectionFileName << endl;
					else
						cout << "No Projection filename specified." << endl;
						break;
					
					this->W2IF = vtkWindowToImageFilter::New();
					this->Writer = vtkPNGWriter::New();
					//this->RenWin->Print(cout);
					this->W2IF->SetInput(this->RenWin);
					//this->W2IF->Print(cout);
					this->Writer->SetInputConnection(this->W2IF->GetOutputPort());
					this->Writer->SetFileName(this->ProjectionFileName);
					//this->Writer->Print(cout);
					this->Writer->Write();
					std::cout << "Projection Saved: " << this->ProjectionFileName << std::endl;
					//this->Writer->Update();

				break;
				}
			}
    }
private: 
	// Name of projection file
	char *ProjectionFileName;
  // Pointer to the interactor
  vtkRenderWindowInteractor *Interactor;
	// Pointer to the render window
  vtkRenderWindow *RenWin;
	// Window to Image Filter
  vtkWindowToImageFilter *W2IF;
	// PNG 2D image writer
	vtkPNGWriter *Writer;
};

int main(int argc, char *argv[])
{
  //Read in vtk filename from command line
	if (argc <= 2) {
		usage();
		return 0;}

  char *volumeFileName = argv[1];
	std::cout << "Volume: " << volumeFileName << std::endl;

  char *projectionFileName = argv[2];
	std::cout << "Projection: " << projectionFileName << std::endl;

	// This opens a 3D dataset in mha format.
  //
  vtkMetaImageReader *volumeReader = vtkMetaImageReader::New();
	volumeReader->SetFileName(volumeFileName);
	volumeReader->Update();

	// The mapper is responsible for pushing the geometry into the graphics
  // library. It may also do color mapping, if scalars or other attributes
  // are defined.
  //
	vtkVolumeTextureMapper3D  *volumeMapper = vtkVolumeTextureMapper3D::New();
  
  volumeMapper->SetInputConnection(volumeReader->GetOutputPort());

  // Apply the texture mapper to the volume
  vtkVolume *volumeActor = vtkVolume::New();
  volumeActor->SetMapper(volumeMapper);
						
  //These are the key settings for changing the way the the resulting DRR looks!
  vtkColorTransferFunction *ctf = vtkColorTransferFunction::New();
  vtkPiecewiseFunction     *otf = vtkPiecewiseFunction::New();

  //Setup the Color Transfer function to threshold anything but bone
  ctf->AddRGBPoint(0.0, 0.0, 0.0, 0.0);
  ctf->AddRGBPoint(72.0 , 1.0, 1.0, 1.0);
  ctf->AddRGBPoint(255.0, 1.0, 1.0, 1.0);

  //Setup the Opacity Transfer function to make the bone look semi-tranparent
  otf->AddPoint(0.0, 0.0);
  otf->AddPoint(70.0 , 0.0);
  otf->AddPoint(255.0, 0.5);
  
  volumeActor->GetProperty()->SetColor(ctf);
  volumeActor->GetProperty()->SetScalarOpacity(otf);
	volumeActor->GetProperty()->SetInterpolationTypeToLinear();
  volumeActor->GetProperty()->SetShade(1);

  // Create the graphics structure. The renderer renders into the 
  // render window. The render window interactor captures mouse events
  // and will perform appropriate camera or actor manipulation
  // depending on the nature of the events.
  //
  vtkMyCallback *myCallback = vtkMyCallback::New();

  vtkRenderer *ren1 = vtkRenderer::New();
  vtkRenderWindow *renWin = vtkRenderWindow::New();
  renWin->AddRenderer(ren1);
  vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
  iren->SetRenderWindow(renWin);
	vtkInteractorStyleTrackballCamera *style = vtkInteractorStyleTrackballCamera::New();
	iren->SetInteractorStyle(style);

	//Set some objects in the callback
	myCallback->SetFilename(projectionFileName);
	myCallback->SetInteractor(iren);
	myCallback->SetRenderWindow(renWin);

  // Add the actors to the renderer, set the background and size
  //
  ren1->AddVolume(volumeActor);
  //ren1->SetBackground(0.0, 0.0, 0.0);
  renWin->SetSize(512, 512);

  // We'll zoom in a little by accessing the camera and invoking a "Zoom"
  // method on it.
	std::cout << "Rendering Supported?: " << int(volumeMapper->IsRenderSupported(volumeActor->GetProperty())) << std::endl;
  ren1->ResetCamera();
  renWin->Render();
	renWin->SetWindowName("DRR");

	//DEBUG Projection -- DONE
	//vtkWindowToImageFilter *w2if = vtkWindowToImageFilter::New();
	//vtkJPEGWriter *writer = vtkJPEGWriter::New();
	//w2if->SetInput(renWin);
	//writer->SetInputConnection(w2if->GetOutputPort());
	//writer->SetFileName("projection_test.jpg");
	//writer->Write();
	//writer->Print(cout);
	//writer->Update();

	
	iren->AddObserver(vtkCommand::CharEvent, myCallback);
  
  // This starts the event loop and as a side effect causes an initial render.
  iren->Start();

	std::cout << "Done!" << std::endl;

  // Exiting from here, we have to delete all the instances that
  // have been created.
  volumeReader->Delete();
  volumeMapper->Delete();
  volumeActor->Delete();
  ren1->Delete();
  renWin->Delete();
  iren->Delete();

  return 0;
}





